package nfwx.analytics;

import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Environment;
import android.os.PowerManager;
import android.os.StatFs;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;


/**
 * 采集设备信息并填充GrabAppSession
 */

public class GrabAppSessionFiller {

    static void fill(GrabAPPSession session, Context ctx)
    {
        //填充版本信息
        try {
            PackageInfo info = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), PackageManager.GET_SIGNATURES);
            session.app.ver.setValue(info.versionName);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        //操作系统
        session.device.os.setValue("android");
        session.device.osv.setValue(Build.VERSION.RELEASE);
        session.device.lang.setValue(Locale.getDefault().getLanguage());
        //cpu内存
        Runtime rt = Runtime.getRuntime();
        int cores=getNumberOfCPUCores();
        if (cores <= 0) cores = rt.availableProcessors();
        session.device.cpu.setValue(cores);
        session.device.model.setValue(Build.MODEL);
        session.device.memSize.setValue(getMemSize(ctx));
        session.device.cpuHZ.setValue(getCPUMaxFreqGHz());

        File f = new File("/sys/class/net/eth0");
        session.device.eth.setValue(f.exists());


        session.device.diskSize.setValue(getDiskSize());
        session.device.diskFree.setValue(getDiskFree());
        session.device.density.setValue(getSystemDensity(ctx));
        session.device.fontScale.setValue(getFontScale());

        DisplayMetrics dm = ctx.getResources().getDisplayMetrics();
        session.device.screenWidth.setValue(dm.widthPixels);
        session.device.screenHeight.setValue(dm.heightPixels);
        try {
            getWiFi(ctx, session);
        } catch(Throwable e) {
            e.printStackTrace();
        }
        try {
            getNet(ctx, session);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        try {
            getCell(ctx, session);
        } catch(Throwable e) {
            e.printStackTrace();
        }
        try {
            getGeo(ctx, session);
        } catch(Throwable e) {
            e.printStackTrace();
        }
        try {
            getBat(ctx, session);
        } catch(Throwable e) {
            e.printStackTrace();
        }
        try {
            getAudio(ctx, session);
        } catch(Throwable e) {
            e.printStackTrace();
        }
        try {
            getBt(ctx, session);
        } catch(Throwable e) {
            e.printStackTrace();
        }

        //音视频编码器
        Set<String> s = getSupportedVideoCodecList();
        for (String str : s) {
           session.device.videoCodecs.push(str);
        }
        s = getSupportedAudioCodecList();
        for (String str : s) {
            session.device.audioCodecs.push(str);
        }
    }

    static double getMemSize(Context ctx)
    {
        double totalMemory = 0;//MB
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
            ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
            ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
            am.getMemoryInfo(mi);
            totalMemory = mi.totalMem / 1024 / 1024;
        }else{
            totalMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
        }
        totalMemory /= 1024; //GB
        return (Math.round(totalMemory * 10)) / 10.0;
    }


    static final int DEVICEINFO_UNKNOWN = -1;

    static protected int getNumberOfCPUCores() {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
            // Gingerbread doesn't support giving a single application access to both cores, but a
            // handful of devices (Atrix 4G and Droid X2 for example) were released with a dual-core
            // chipset and Gingerbread; that can let an app in the background run without impacting
            // the foreground application. But for our purposes, it makes them single core.
            return 1;
        }
        int cores=-1;
        try {
            cores = new File("/sys/devices/system/cpu/").listFiles(CPU_FILTER).length;
        } catch (SecurityException e) {
            cores = -1;
        } catch (NullPointerException e) {
            cores = -1;
        }
        return cores;
    }

    static private FileFilter CPU_FILTER = new FileFilter() {

        public boolean accept(File pathname) {
            String path = pathname.getName();
            //regex is slow, so checking char by char.
            if (path.startsWith("cpu")) {
                for (int i = 3; i < path.length(); i++) {
                    if (path.charAt(i) < '0' || path.charAt(i) > '9') {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
    };

    //获取cpu最大频率
    static protected double getCPUMaxFreqGHz() {
        float maxFreq = DEVICEINFO_UNKNOWN;
        try {
            for (int i = 0; i < 8; i++) {
                String filename =
                        "/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq";
                File cpuInfoMaxFreqFile = new File(filename);
                if (cpuInfoMaxFreqFile.exists()) {
                    byte[] buffer = new byte[128];
                    FileInputStream stream = new FileInputStream(cpuInfoMaxFreqFile);
                    try {
                        stream.read(buffer);
                        int endIndex = 0;
                        //Trim the first number out of the byte buffer.
                        while (buffer[endIndex] >= '0' && buffer[endIndex] <= '9'
                                && endIndex < buffer.length) endIndex++;
                        String str = new String(buffer, 0, endIndex);
                        Integer freqBound = Integer.parseInt(str);
                        if (freqBound > maxFreq) maxFreq = ((float) freqBound) / 1000 / 1000;
                    } catch (NumberFormatException e) {
                        //Fall through and use /proc/cpuinfo.
                    } finally {
                        stream.close();
                    }
                }
            }
            if (maxFreq == DEVICEINFO_UNKNOWN) {
                FileInputStream stream = new FileInputStream("/proc/cpuinfo");
                try {
                    float freqBound = parseFileForValue("cpu MHz", stream);
                    freqBound /= 1000; //MHz -> GHz
                    if (freqBound > maxFreq) maxFreq = freqBound;
                } finally {
                    stream.close();
                }
            }
        } catch (IOException e) {
            maxFreq = DEVICEINFO_UNKNOWN; //Fall through and return unknown.
        }
        return (Math.round(maxFreq * 10)) / 10.0;
    }

    static private int parseFileForValue(String textToMatch, FileInputStream stream) {
        byte[] buffer = new byte[1024];
        try {
            int length = stream.read(buffer);
            for (int i = 0; i < length; i++) {
                if (buffer[i] == '\n' || i == 0) {
                    if (buffer[i] == '\n') i++;
                    for (int j = i; j < length; j++) {
                        int textIndex = j - i;
                        //Text doesn't match query at some point.
                        if (buffer[j] != textToMatch.charAt(textIndex)) {
                            break;
                        }
                        //Text matches query here.
                        if (textIndex == textToMatch.length() - 1) {
                            return extractValue(buffer, j);
                        }
                    }
                }
            }
        } catch (IOException e) {
            //Ignore any exceptions and fall through to return unknown value.
        } catch (NumberFormatException e) {
        }
        return DEVICEINFO_UNKNOWN;
    }

    static private int extractValue(byte[] buffer, int index) {
        while (index < buffer.length && buffer[index] != '\n') {
            if (buffer[index] >= '0' && buffer[index] <= '9') {
                int start = index;
                index++;
                while (index < buffer.length && buffer[index] >= '0' && buffer[index] <= '9') {
                    index++;
                }
                String str = new String(buffer, 0, start, index - start);
                return Integer.parseInt(str);
            }
            index++;
        }
        return DEVICEINFO_UNKNOWN;
    }

    static protected boolean isExternalCardMount() {
        String state = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals(state) ? true : false;
    }

    //diskSize	磁盘总容量
    static protected double getDiskSize() {
        if (isExternalCardMount()) {
            File sdcardDir = Environment.getExternalStorageDirectory();
            StatFs sf = new StatFs(sdcardDir.getPath());
            double blockSize = sf.getBlockSize();
            double blockCount = sf.getBlockCount();
            double diskSize = blockSize * (blockCount / 1024) / 1024 / 1024;//G
            return (float) (Math.round(diskSize * 10)) / 10.0;
        }
        return 0;
    }

    //diskFree	磁盘的可用容量
    static protected double getDiskFree() {
        if (isExternalCardMount()) {
            File sdcardDir = Environment.getExternalStorageDirectory();
            StatFs sf = new StatFs(sdcardDir.getPath());
            double blockSize = (double)sf.getBlockSize();
            if (android.os.Build.VERSION.SDK_INT >= 18) {
                blockSize = sf.getBlockSizeLong();
            }
            double availCount = (double)sf.getAvailableBlocks();
            if (android.os.Build.VERSION.SDK_INT >= 18) {
                availCount = sf.getAvailableBlocksLong();
            }
            double diskFree = availCount * blockSize / 1024 / 1024 / 1024;//G
            return (double) (Math.round(diskFree * 100)) / 100.0;
        }
        return 0;
    }

    static protected int getSystemDensity(Context context) {
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        int dens = dm.densityDpi;
        return (dens);
    }

    /**
     * 获取字体比例1为1:1无缩放
     * 过读取系统属性设置字体缩放的默认值，ro.sys.def_font_scale在build.prop文件里设置
     * fontScale = (float)(SystemProperties.getInt("ro.sys.def_font_scale", 100))/100;
     */
    protected static float getFontScale() {
        float scale = 1;
        try {
            Class<?> c = Class.forName("android.os.SystemProperties");

            try {
                Method get = c.getMethod("getInt", new Class[]{String.class, int.class});

//               Method[] mds = c.getMethods();
//               for(Method md : mds){
//                   Utils.Debug(Utils.TAG,md.getName());
//               }
                get.setAccessible(true);
                scale = ((int)get.invoke(c.newInstance(), "ro.sys.def_font_scale", 100)) / 100.0f;

            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return scale;
    }

    private static boolean getCell(Context ctx, GrabAPPSession session) {
        GrabCellInfo cell = session.cell;
        try {
            TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
            if (tm == null) {
                cell.exists.setValue(false);
                return false;
            }
            cell.exists.setValue(true);
            cell.roaming.setValue(tm.isNetworkRoaming());
            int dv = getDataEnabled(ctx);
            if (dv != -1) {
                cell.data.setValue(dv == 1);
                session.device.cell.setValue(dv == 1);
            }
            cell.type.setValue(getNetworkClass(tm.getNetworkType()));
            cell.provider.setValue(tm.getSimOperatorName());
            return true; //tm.getPhoneType() != PHONE_TYPE_NONE;
        } catch (Throwable e) {
            e.printStackTrace();
            return false;
        }
    }

    private static int getDataEnabled(Context ctx)
    {
        Class[] getArgArray = null;
        Object[] getArgInvoke = null;

        try {
            ConnectivityManager conManager = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
            Method mGetMethod = conManager.getClass().getMethod("getMobileDataEnabled", getArgArray);
            boolean isOpen = (Boolean) mGetMethod.invoke(conManager, getArgInvoke);
            return isOpen?1:0;
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            TelephonyManager teleManager = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
            Method mGetMethod = teleManager.getClass().getMethod("getDataEnabled", getArgArray);
            boolean isOpen = (Boolean) mGetMethod.invoke(teleManager, getArgInvoke);
            return isOpen?1:0;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    private static String getNetworkClass(int networkType) {
        switch (networkType) {
            case TelephonyManager.NETWORK_TYPE_GPRS:
            case TelephonyManager.NETWORK_TYPE_EDGE:
            case TelephonyManager.NETWORK_TYPE_CDMA:
            case TelephonyManager.NETWORK_TYPE_1xRTT:
            case TelephonyManager.NETWORK_TYPE_IDEN:
                return "2g";
            case TelephonyManager.NETWORK_TYPE_UMTS:
            case TelephonyManager.NETWORK_TYPE_EVDO_0:
            case TelephonyManager.NETWORK_TYPE_EVDO_A:
            case TelephonyManager.NETWORK_TYPE_HSDPA:
            case TelephonyManager.NETWORK_TYPE_HSUPA:
            case TelephonyManager.NETWORK_TYPE_HSPA:
            case TelephonyManager.NETWORK_TYPE_EVDO_B:
            case TelephonyManager.NETWORK_TYPE_EHRPD:
            case TelephonyManager.NETWORK_TYPE_HSPAP:
                return "3g";
            case TelephonyManager.NETWORK_TYPE_LTE:
                return "4g";
            default:
                return "un";
        }
    }

    private static void getWiFi(Context ctx, GrabAPPSession session)
    {
        WifiManager wifi = null;
        GrabWiFiInfo wInfo = session.wifi;
        try {
             wifi = (WifiManager) ctx.getSystemService(Context.WIFI_SERVICE);
            if (wifi == null) {
                session.device.wifi.setValue(false);
                wInfo.exists.setValue(false);
                return;
            }
            session.device.wifi.setValue(true);
            wInfo.exists.setValue(true);
            wInfo.enabled.setValue(wifi.isWifiEnabled());
            WifiInfo info = wifi.getConnectionInfo();
            wInfo.connected.setValue(info != null);
            if (info == null) return;
            String ssid=info.getSSID();
            if (ssid != null && ssid.length() >= 2) {
                ssid = ssid.substring(1, ssid.length()-1);
            }
            wInfo.SSID.setValue(ssid);
            wInfo.BSSID.setValue(info.getBSSID());
            wInfo.speed.setValue(info.getLinkSpeed());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private static void getNet(Context ctx, GrabAPPSession session)
    {
        GrabNetInfo net = session.net;
        ConnectivityManager conn = (ConnectivityManager)ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
        //获取本机IP
        try
        {
            boolean quit = false;
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements() && !quit;)
            {
                NetworkInterface intf = en.nextElement();
                if (!intf.isUp() || intf.getInterfaceAddresses().size() == 0) continue;
                if (intf.isLoopback()) continue;
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();)
                {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address)
                    {
                        net.localIP.setValue(inetAddress.getHostAddress().toString());
                        quit = true;
                        break;
                    }
                }
            }
        }
        catch (SocketException ex)
        {
            ;
        }
        if (!net.localIP.getHasValue()) { //ipv4地址未设置, 尝试获取v6
            try
            {
                boolean quit = false;
                for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements() && !quit;)
                {
                    NetworkInterface intf = en.nextElement();
                    if (!intf.isUp() || intf.getInterfaceAddresses().size() == 0) continue;
                    if (intf.isLoopback()) continue;
                    for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();)
                    {
                        InetAddress inetAddress = enumIpAddr.nextElement();
                        if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet6Address)
                        {
                            net.localIP.setValue(inetAddress.getHostAddress().toString());
                            quit = true;
                            break;
                        }
                    }
                }
            }
            catch (SocketException ex)
            {
                ;
            }
        }

        //获取代理设置
        if (Build.VERSION.SDK_INT >= 23) {
            net.proxy.setValue(conn.getDefaultProxy() != null);
        } else {
            net.proxy.setValue(android.net.Proxy.getHost(ctx) != null);
        }

        //获取VPN
        try
        {
            boolean quit = false;
            boolean vpn=false;
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements() && !quit;)
            {
                NetworkInterface intf = en.nextElement();
                if (!intf.isUp() || intf.getInterfaceAddresses().size() == 0) continue;
                if (intf.isLoopback()) continue;
                if ("tun0".equals(intf.getName()) || "ppp0".equals(intf.getName())){
                    vpn = true;
                }
            }
            net.vpn.setValue(vpn);
        }
        catch (SocketException ex)
        {
            ;
        }

    }

    private static void getGeo(Context ctx, final GrabAPPSession session)
    {
        GrabGeoInfo geo = session.geo;
        TimeZone tz = TimeZone.getDefault();
        geo.tz.setValue(tz.getID());
        new AsyncTask<Void,Void,JSONObject>(){
            @Override
            protected JSONObject doInBackground(Void... params) {
                String s = GrabHttp.httpGET("http://ip-api.com/json/");
                if (s == null) return null;
                try {
                    JSONObject o = new JSONObject(s);
                    if (!("success".equals(o.getString("status")))) {
                        return null;
                    }
                    return o;
                } catch(JSONException e) {
                    e.printStackTrace();
                    return null;
                }
            }

            @Override
            protected void onPostExecute(JSONObject obj) {
                if (obj == null) {
                    return;
                }
                GrabGeoInfo geo = session.geo;
                geo.exists.setValue(true);
                geo.country.setValue(obj.optString("country", ""));
                geo.state.setValue(obj.optString("regionName", ""));
                geo.city.setValue(obj.optString("city", ""));
                double lat = obj.optDouble("lat");
                double lon = obj.optDouble("lon");
                if (Double.isNaN(lat) || Double.isNaN(lon)) {
                    ;
                } else {
                    geo.pt.clear();
                    geo.pt.push(lon);
                    geo.pt.push(lat);
                }
                session.net.isp.setValue(obj.optString("isp"));
                session.net.as.setValue(obj.optString("as"));
            }
        }.execute();
    }


    static void getBat(Context ctx, GrabAPPSession session)
    {
        IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        Intent batteryStatus = ctx.registerReceiver(null, ifilter);
        if (!batteryStatus.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false)) {
            session.bat.exists.setValue(false);
            return;
        }
        session.bat.exists.setValue(true);
        int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
        session.bat.charging.setValue(chargePlug != 0);
        int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
        int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
        session.bat.level.setValue(Math.round(level * 100.0/ scale));
        session.bat.exists.setValue(true);
        if (Build.VERSION.SDK_INT >= 21) {
            PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
            session.bat.powerSave.setValue(pm.isPowerSaveMode());
        }
    }

    static void getAudio(Context ctx, GrabAPPSession session)
    {
        GrabAudioInfo aud = session.audio;
        AudioManager am = (AudioManager) ctx.getSystemService(Context.AUDIO_SERVICE);
        if (am == null) {
            aud.exists.setValue(false);
            return;
        }
        if (Build.VERSION.SDK_INT >= 23) {
            AudioDeviceInfo[] aInfo = am.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
            if (aInfo != null && aInfo.length != 0) {
                int[] chnls = aInfo[0].getChannelCounts();
                int maxChnls=0;
                for(int i=0; i<chnls.length; ++i) {
                    if (chnls[i] > maxChnls) maxChnls = chnls[i];
                }
                aud.channels.setValue(maxChnls);
                aud.name.setValue(""+aInfo[0].getProductName());
                int[] srs = aInfo[0].getSampleRates();
                int maxSr=0;
                for(int i=0; i<srs.length; ++i) {
                    if (srs[i] > maxSr) maxSr = srs[i];
                }
                aud.samplerate.setValue(maxSr);
                int[] encs = aInfo[0].getEncodings();
                int maxBits=0;
                for(int i=0; i<encs.length; ++i) {
                    switch(encs[i]) {
                        case AudioFormat.ENCODING_PCM_8BIT:
                            if (maxBits < 8) maxBits = 8;
                            break;
                        case AudioFormat.ENCODING_PCM_16BIT:
                            if (maxBits < 16) maxBits = 16;
                            break;
                        case AudioFormat.ENCODING_PCM_FLOAT:
                            if (maxBits < 32) maxBits = 32;
                            break;
                        case AudioFormat.ENCODING_AC3:
                        case AudioFormat.ENCODING_E_AC3:
                        case AudioFormat.ENCODING_DTS:
                        case AudioFormat.ENCODING_DTS_HD:
                        case AudioFormat.ENCODING_IEC61937:
                            maxBits = 1024; //支持高清格式的音频，我们设置为1024
                            break;
                    }
                }
                aud.bits.setValue(maxBits);
            }
        }

        int level = am.getStreamVolume(AudioManager.STREAM_MUSIC);
        int maxLevel = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        aud.volume.setValue((int)(level*100 / maxLevel));
        if (am.isBluetoothA2dpOn()) { //蓝牙耳机
            aud.state.setValue(2);
        } else if (am.isWiredHeadsetOn()) {
            aud.state.setValue(1);
        } else {
            aud.state.setValue(0);
        }
    }

    private static void getBt(Context ctx, GrabAPPSession session)
    {
        GrabBlueThooth bt = session.bt;
        BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
        if (ba == null) {
            bt.exists.setValue(false);
            return;
        }
        try {
            if (!ba.isEnabled()) {
                bt.connected.setValue(false);
            }
            bt.name.setValue(ba.getName());
            if (ba.getProfileConnectionState(BluetoothProfile.A2DP) == BluetoothProfile.STATE_CONNECTED) {
                bt.connected.setValue(true);
            }
            if (ba.getProfileConnectionState(BluetoothProfile.HEADSET) == BluetoothProfile.STATE_CONNECTED) {
                bt.connected.setValue(true);
            }
            if (ba.getProfileConnectionState(BluetoothProfile.HEALTH) == BluetoothProfile.STATE_CONNECTED) {
                bt.connected.setValue(true);
            }
            bt.exists.setValue(true);
        } catch(Throwable e) {
            e.printStackTrace();
        }
    }

    private static Set<String> getSupportedVideoCodecList()
    {
        Set<String> r = new TreeSet<>();
        if (Build.VERSION.SDK_INT < 16) return r;
        boolean newCodecs = Build.VERSION.SDK_INT >= 21;
        int n;
        MediaCodecList m = null;
        MediaCodecInfo[] infos=null;
        if (newCodecs) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                m = new MediaCodecList(MediaCodecList.ALL_CODECS);
                infos = m.getCodecInfos();
            }
            n = infos.length;
        } else {
            n = MediaCodecList.getCodecCount();
        }
        for(int i=0; i<n; ++i) {
            MediaCodecInfo info;
            if (newCodecs) {
                info = infos[i];
            } else {
                info = MediaCodecList.getCodecInfoAt(i);
            }
            if (info.isEncoder()) continue;
            String[] types = info.getSupportedTypes();
            String typesStr="";
            for(int k=0; types != null && k<types.length; ++k) {
                typesStr += types[k];
                typesStr += ",";
            }
            String name=info.getName();
            if (types == null || types.length == 0) continue;
            for(int j=0; j<types.length; ++j) {
                if (types[j].startsWith("video/")) {
                    r.add(types[j]);
                }
            }
        }
        return r;
    }

    private static Set<String> getSupportedAudioCodecList()
    {
        Set<String> r = new TreeSet<>();
        if (Build.VERSION.SDK_INT < 16) return r;
        boolean newCodecs = Build.VERSION.SDK_INT >= 21;
        int n;
        MediaCodecList m = null;
        MediaCodecInfo[] infos=null;
        if (newCodecs) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                m = new MediaCodecList(MediaCodecList.ALL_CODECS);
                infos = m.getCodecInfos();
            }
            n = infos.length;
        } else {
            n = MediaCodecList.getCodecCount();
        }
        for(int i=0; i<n; ++i) {
            MediaCodecInfo info;
            if (newCodecs) {
                info = infos[i];
            } else {
                info = MediaCodecList.getCodecInfoAt(i);
            }
            if (info.isEncoder()) continue;
            String[] types = info.getSupportedTypes();
            String typesStr="";
            for(int k=0; types != null && k<types.length; ++k) {
                typesStr += types[k];
                typesStr += ",";
            }
            String name=info.getName();
            if (types == null || types.length == 0) continue;
            for(int j=0; j<types.length; ++j) {
                if (types[j].startsWith("audio/")) {
                    r.add(types[j]);
                }
            }
        }
        return r;
    }


}
